"""
Author        yongfa
Date          2022-07-14 11:50:08
LastEditTime  2023-04-04 17:48:35
LastEditors   yongfa
Description   基础服务模块
"""

from flask import g, current_app

from app.extensions.sqlalchemy import pagination_to_dict, model_list_to_dict
from app.extensions.exception import Failed, Success
from app.extensions.utils.validator import validate_columns


class SimpleService(object):

    def __init__(self, model=None, args=None) -> None:
        self.model = model
        self.args = args.parse_args() if args is not None else {}


class BaseService():
    """通用的服务类"""

    def __init__(self,
                 model=None,
                 args=None,
                 filter_user: bool = False,
                 filter_delete: bool = False,
                 only_parent: bool = True,
                 is_desc: bool = False
                 ) -> None:
        """
        Description:    解析请求参数，预处理查询条件

        Parameters:     参数说明
           args         请求参数
           filter_user  是否根据user_id 过滤
           filter_delete  是否根据delete_time 过滤
           is_desc      根据id降序

        """
        self.model = model
        self.args = args.parse_args() if args is not None else {}
        self.filter_user = filter_user
        self.filter_delete = filter_delete
        self.only_parent = only_parent
        self.is_desc = is_desc
        self.conditions = {}
        self.DEFAULT_DELETE_TIME = current_app.config.get('DEFAULT_DELETE_TIME')  # 默认删除时间

        if self.filter_user:
            self.conditions.update({'user_id': g.get('user_id', None)})

        if self.filter_delete:
            self.conditions.update({'delete_time': current_app.config.get('DEFAULT_DELETE_TIME')})

    def get_all(self):
        """不分页"""
        queryset = self.get_all_query()
        data = model_list_to_dict(queryset.all())
        return Success(data)

    def get_list(self):
        """分页列表"""
        page = self.args.get('page', 1)
        size = self.args.get('size', 20)
        queryset = self.get_all_query()
        paginator = queryset.paginate(page=page, per_page=size, error_out=True)
        data = pagination_to_dict(paginator)
        return Success(data)

    def get_detail(self, id):
        """详情"""
        conditions = {'id': id}
        if self.conditions:
            conditions.update(self.conditions)
        item = self.model.get_or_404(**conditions)
        return Success(item)

    def create(self):
        """创建"""
        data = validate_columns(self.model, self.args)
        if not data:
            return Failed(message='请求参数不能为空！')
        if self.conditions:
            data.update(self.conditions)
        self.model.create(**data)
        return Success()

    def update(self, id):
        """更新"""
        data = validate_columns(self.model, self.args)
        if not data:
            return Failed(message='请求参数不能为空！')
        conditions = {'id': id}
        if self.conditions:
            conditions.update(self.conditions)
        item = self.model.get_or_404(**conditions)
        item.update(**data)
        return Success()

    def delete(self, id):
        """硬删除"""
        conditions = {'id': id}
        if self.conditions:
            conditions.update(self.conditions)
        item = self.model.get_or_404(**conditions)
        item.hard_delete()
        return Success()

    def soft_delete(self, id):
        """软删除"""
        conditions = {'id': id}
        if self.conditions:
            conditions.update(self.conditions)
        item = self.model.get_or_404(**conditions)
        item.soft_delete()
        return Success()

    def get_all_query(self, query_time: str = 'create_time', search_fields: list = []):
        """_summary_

        Args:
            query_time (str, optional): 时间范围键值. Defaults to 'create_time'.
            search_fields (list, optional): 模糊. Defaults to [].

        Raises:
            ValueError: _description_

        Returns:
            queryset: sqlalchemy.orm.Query
        """

        queryset = self.model.query.filter()

        # 模糊查询
        search_fields = ['label', 'name', 'code', 'title', 'waybillNumber'] if not search_fields else search_fields
        for field in search_fields:
            field_arg = self.args.pop(field) if hasattr(self.args, field) else None
            if field_arg is not None and hasattr(self.model, field):
                queryset = queryset.filter(getattr(self.model, field).ilike("%{}%".format(field_arg)))

        # 精确条件(去除None值)
        conditions = validate_columns(self.model, self.args)
        if self.conditions:
            conditions.update(self.conditions)

        # 多层级查询
        if self.only_parent:
            if hasattr(self.model, 'parent_id') and 'parent_id' not in conditions.keys():
                conditions.update({'parent_id': None})

        # 排序 按id降序
        if self.is_desc:
            queryset = queryset.order_by(self.model.id.desc())

        queryset = queryset.filter_by(**conditions)

        # 时间区间搜索
        start_time = self.args.pop('start_time') if self.args.get('start_time') else None
        end_time = self.args.pop('end_time') if self.args.get('end_time') else None
        if start_time and end_time:
            if not hasattr(self.model, query_time):
                raise ValueError('搜索的 {} 时间属性不在对象中'.format(query_time))
            queryset = queryset.filter(getattr(self.model, query_time).between(start_time, end_time))

        return queryset

    def get_tree(self, output_fields=[], exclude_fields=[]):
        """树形数据"""
        conditions = validate_columns(self.model, self.args)
        if self.conditions:
            conditions.update(self.conditions)

        data = self.model.query.filter_by(**conditions).all()
        parent_id = self.args.get('parent_id', None)
        parent = None
        if parent_id is not None:
            parent = self.model.get(id=parent_id)
            if parent is None:
                raise Failed(message='该父节点不存在')

        default_output_fields = ['id', 'name', 'value', 'label', 'parent_id', 'state', 'path']
        output_fields = list(set(default_output_fields).intersection(set(output_fields))) \
            if output_fields else default_output_fields
        output_fields = [] if not output_fields else output_fields
        result = self.build_tree(data, parent, output_fields, exclude_fields)
        return Success(result=result)

    @staticmethod
    def build_tree(data, parent, output_fields=[], exclude_fields=[]):
        """树形数据"""
        result = Tree(data, parent, output_fields, exclude_fields).build_tree()
        return result


class Tree:

    def __init__(self, data, parent=None, output_fields=[], exclude_fields=[]) -> None:
        self.data = data
        self.parent = parent
        self.root_node = list()
        self.common_node = dict()
        self.tree = list()
        self.output_fields = output_fields
        self.exclude_fields = exclude_fields

    def find_root_node(self) -> list:
        """
        查找根节点
        :return:根节点列表
        """
        # self.root_node = list(filter(lambda x: x["parent_id"] is None, data))
        if self.parent:
            self.root_node.append(self.parent)
        else:
            for node in self.data:
                # 假定parent_id是None就是根节点
                # 例如有些数据库设计会直接把根节点标识出来
                if node.parent_id is None:
                    self.root_node.append(node)
        return self.root_node

    def find_common_node(self) -> dict:
        """
        寻找共同的父节点
        :return: 共同的父节点字典
        """

        for node in self.data:
            parent_id = node.parent_id
            # 排除根节点情况
            if parent_id is not None:
                # 如果父节点ID不在字典中则添加到字典中
                if parent_id not in self.common_node:
                    self.common_node[parent_id] = list()
                self.common_node[parent_id].append(node)
        return self.common_node

    def build_tree(self) -> list:
        """
        生成目录树
        :return:
        """
        self.find_root_node()
        self.find_common_node()
        for root in self.root_node:
            # 生成字典
            base = dict(root.to_dict(output_fields=self.output_fields,
                        exclude_fields=self.exclude_fields), children=list())
            base["id"] = base.get("id") if base.get("id") else base.get("dept_id")
            # 遍历查询子节点
            self.find_child(base["id"], base["children"])
            # 添加到列表
            self.tree.append(base)
        return self.tree

    def find_child(self, parent_id: int, child_node: list):
        """
        查找子节点
        :param parent_id:父级ID
        :param child_node: 父级孩子节点
        :return:
        """
        # 获取共同父节点字典的子节点数据
        child_list = self.common_node.get(parent_id, [])
        for item in child_list:
            # 生成字典
            base = dict(item.to_dict(output_fields=self.output_fields,
                        exclude_fields=self.exclude_fields), children=list())
            # 遍历查询子节点
            self.find_child(item.id, base["children"])
            # 添加到列表
            child_node.append(base)
